package ru.ifmo.croak.impl.replication.team1;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ifmo.croak.api.PostService;
import ru.ifmo.croak.api.QueryService;
import ru.ifmo.croak.impl.replication.team1.io.MulticastMessage;
import ru.ifmo.croak.impl.replication.team1.sync.SynchronizerManager;
import ru.ifmo.croak.impl.replication.team1.sync.SynchronizerTimer;
import ru.ifmo.croak.util.CroakFactory;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.ServerSocket;

public class ReplicationImplMain implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(ReplicationImplMain.class);

    private static final int TIME_TO_LIVE = 255;
    private final LocalContext localContext;

    private final String address;


    public ReplicationImplMain(String name) {
        String providerName = CroakFactory.getInstance().getProperty(name + ".provider");
        PostService postService = CroakFactory.getInstance().getImplementation(PostService.class, providerName);
        QueryService queryService = CroakFactory.getInstance().getImplementation(QueryService.class, providerName);
        localContext = new LocalContext(postService, queryService);
        address = CroakFactory.getInstance().getProperty(name + ".address");
    }

    public void run() {
        try {
            InetAddress group = InetAddress.getByName(address.split(":")[0]);
            int port = Integer.parseInt(address.split(":")[1]);


            ServerSocket synchronizerServerSocket = new ServerSocket(0);
            localContext.setSynchronizerTCPPort(synchronizerServerSocket.getLocalPort());

            MulticastSocket sendingSocket = new MulticastSocket();
            sendingSocket.setTimeToLive(TIME_TO_LIVE);
            Sender sender = new Sender(sendingSocket, localContext, new InetSocketAddress(group, port));
            Thread senderThread = new Thread(sender, "senderThread");
            senderThread.start();


            SynchronizerManager synchronizer = new SynchronizerManager(synchronizerServerSocket, localContext);
            Thread synchronizerThread = new Thread(synchronizer, "synchronizerThread");
            synchronizerThread.start();
            localContext.setSynchronizerManager(synchronizer);

            MulticastSocket receivingSocket = new MulticastSocket(port);
            receivingSocket.joinGroup(group);
            Receiver receiver = new Receiver(receivingSocket, localContext);
            Thread receiverThread = new Thread(receiver, "receiverThread");
            receiverThread.start();

            SynchronizerTimer synchronizerTimer = new SynchronizerTimer(synchronizer, localContext);
            Thread synchronizerTimerThread = new Thread(synchronizerTimer, "synchronizerTimerThread");
            synchronizerTimerThread.start();
            sender.sendSystemMessage(MulticastMessage.MessageType.UPDATE_REQUEST);

            Pinger pinger = new Pinger(sender);
            Thread pingerThread = new Thread(pinger, "pinger");
            pingerThread.start();

        } catch (IOException e) {
            log.error("IOException happens", e);
        }
    }
}
